package com.xiyuan;

import com.xiyuan.config.Config;
import com.xiyuan.config.Group;
import com.xiyuan.config.Item;
import com.xiyuan.smartutils.*;
import com.xiyuan.smartutils.annotation.AnAlias;
import com.xiyuan.smartutils.constants.SysConstants;

import java.util.*;


/**
 * 工程全局变量类<br><br>
 * 1、保存conf下的配置信息，可通过get获取,set设置,save保存至文件<br>
 * 2、注册到全局变量中的单例或多例，并支持获取对象
 * 
 * @version v1.0.0 @author lgz 2017-8-27 新建与整理
 */
@AnAlias("Global")
public final class Global implements SysConstants
{
    
    //全局配置
    private static final Map<String, Config> configMap = new LinkedHashMap<>();
    private static final Map<String, Group> groupMap = new LinkedHashMap<>();
    
    //全局服务列表
    private static final  Map<String, Service> serviceMap = new LinkedHashMap<>();
    
    //全局变量表&全局类别名表
    private static final Map<Class<?>, Object> objMap = new HashMap<Class<?>, Object>();
    private static final Map<String, Class<?>> aliasMap = new HashMap<>();
    
    /** 不允许实例化 */
    private Global(){}
    
    /** 获取全局ClassLoader */
    public static ClassLoader getClassLoader()
    {
        SysClasspath _classpath = get(SysClasspath.class);
        if (_classpath == null)
            return Thread.currentThread().getContextClassLoader();
        else
            return _classpath.getClassLoader();
    }
    
    /********************************************************************************/
    //全局对象实例设置和获取等
    /********************************************************************************/
    
    /** 判断全局变量表中是否有值 */
    public static boolean has(Class<?> cls)
    {
        return objMap.containsKey(cls);
    }
    
    /**
     * 从全局变量表中获取对象，（单例取本身｜多例取第一个）
     * 
     * @param cls   对象类名
     * @return      对象
     */
    @SuppressWarnings("unchecked")
    public static <T> T get(Class<T> cls)
    {
        Object obj = objMap.get(cls);
        if (obj == null)
            return null;
        
        if (!Classes.isImplement(cls, MultiInstance.class))
        {//未实现多例的，取当前值
            return (T)obj;
        }
        
        //实现多例的，转为列表，为空null，有取第一个
        List<?> list = (List<?>)obj;
        return (list.isEmpty())?null:(T)list.get(0);
    }
    
    /**
     * 从全局变量表中获取多例中的一个，（单例时返回null｜多例索引号不正确返回null｜多例索引号正确取值）
     * 
     * @param cls   对象类名
     * @param idx   多例索引
     * @return      对象
     */
    @SuppressWarnings("unchecked")
    public static <T> T get(Class<T> cls, int idx)
    {
        Asserts.as(Classes.isImplement(cls, MultiInstance.class) ? null : "全局变量表中，不是多例不支持调用该方法[get(Class<T> cls, int idx)]");
        
        Object obj = objMap.get(cls);
        if (obj == null)
            return null;
        
        List<?> list = (List<?>)obj;
        return (list.isEmpty() || list.size() < (idx+1))?null:(T)list.get(idx);
    }
    

    /**
     * 从全局变量表中获取对象，（如果没有则尝试创建，创建不成功强制抛出IllegalArgumentException）
     * 
     * @param cls   对象类名
     * @return      对象
     */
    public static <T> T getWithoutNew(Class<T> cls)
    {
        T obj = get(cls);
        if (obj != null)
        {//1.存在即返回
            return obj;
        }
        
        synchronized (objMap)
        {
            obj = get(cls);
            if (obj != null)
            {//2.在同步中作双重检查
                return obj;
            }
            
            //3.新建实例，并放置到全局对象表中
            obj = Classes.newInstance(cls);
            Asserts.isNull(obj, SysI18n.classDoesNotSupportNoParameterConstructors, cls.getName());
            
            if (!Classes.isImplement(cls, MultiInstance.class))
            {//单例
                objMap.put(cls, obj);
            }
            else
            {//多例
                MultiInstance instance = (MultiInstance)obj;
                instance.setIndex(0);
                
                ArrayList<MultiInstance> list = new ArrayList<>();
                list.add(instance);
                list.trimToSize();
                objMap.put(cls, list);
            }
            
            return obj;
        }
    }
    
    /**
     * 增加对象到全局变量表中，注意只支持增加，不支持修改
     * 
     * @param cls   对象类名
     * @param obj   对象
     */
    public static <T> boolean add(Class<T> cls, T obj)
    {
        if (!Classes.isImplement(cls, MultiInstance.class))
        {//单例
            synchronized (objMap)
            {
                if (objMap.containsKey(cls))
                    return false;
                
                objMap.put(cls, obj);
            }
        }
        else
        {//多例
            synchronized (objMap)
            {
                @SuppressWarnings("unchecked")
                ArrayList<MultiInstance> list = (ArrayList<MultiInstance>)objMap.get(cls);
                if (list == null)
                {
                    list = new ArrayList<MultiInstance>();
                    objMap.put(cls, list);
                }
                
                MultiInstance instance = (MultiInstance)obj;
                list.add(instance);
                list.trimToSize();
                if (instance != null)
                {//因为允许对象为null，这里要判断
                    instance.setIndex(list.size() - 1);
                }
            }
        }
        
        return true;
    }
    
    /**
     * 从全局变量表中移除指定的对象
     * 
     * @param cls   类结构
     */
    public static void remove(Class<?> cls)
    {
        synchronized (objMap)
        {
            objMap.remove(cls);
        }
    }
    
    /**
     * 从全局变量表中移除指定的对象（单例直接移除｜多例置该索引号的值为null）
     * 
     * @param cls   类结构
     * @param obj   对象
     */
    public static <T> void remove(Class<T> cls, T obj)
    {
        if (!objMap.containsKey(cls))
            return;
        
        if (!Classes.isImplement(cls, MultiInstance.class))
        {//单例
            remove(cls);
            return;
        }
        
        //多例
        List<?> list = (List<?>)objMap.get(cls);
        synchronized (list)
        {
            for (int i=0;i<list.size();i++)
            {
                Object object = list.get(i);
                if (object == obj)
                {//设置该索引号的值为null，判断使用恒等（保证对象实例是同一个，而不是对象值相等）
                    list.set(i, null);
                }
            }
        }
    }
    
    /**
     * 从全局变量表中移除指定的对象（单例直接移除｜多例置该索引号的值为null）
     * 
     * @param cls   类结构
     * @param idx   多例索引值
     */
    public static void remove(Class<?> cls, int idx)
    {
        Asserts.as((cls != null)?null:"删除全局变量时[类结构参数]不能为null");
        Asserts.as((idx >= 0)?null:"删除全局变量多例对象时[索引号]不正确");
        
        if (!objMap.containsKey(cls))
            return;
        
        if (!Classes.isImplement(cls, MultiInstance.class))
        {//单例
            remove(cls);
            return;
        }
        
        //多例
        List<?> list = (List<?>)objMap.get(cls);
        if (list.size() <= idx)
            return;
        
        synchronized (list)
        {//设置该索引号的值为null
            list.set(idx, null);
        }
    }
    
    /********************************************************************************/
    //系统服务设置和获取等
    /********************************************************************************/
    
    /** 增加服务 */
    public static void addService(Service service)
    {
        Asserts.as(!serviceMap.containsKey(service.getId()), SysI18n.globalDuplicateServiceNotSupported);
        serviceMap.put(service.getId(), service);
    }
    
    /** 移除服务 */
    public static void removeService(String id)
    {
        serviceMap.remove(id);
    }
    
    /** 判断是否有服务信息 */
    public static boolean hasService(String id)
    {
        return serviceMap.containsKey(id);
    }
    
    /** 获取配置的服务信息 */
    public static Service getService(String id)
    {
        return serviceMap.get(id);
    }
    
    /** 获取配置的服务信息，并强制转换 */
    @SuppressWarnings("unchecked")
    public static <T> T getService(Class<T> cls, String id)
    {
        if (Validates.isBlank(id))
            return getService(cls);
        
        Service service = serviceMap.get(id);
        return (service != null && cls == service.getClass())?(T)service:null;
    }
    
    /** 获取类名相同的服务第一个 */
    public static <T> T getService(Class<T> cls)
    {
        List<T> list = getServiceListPrivate(cls);
        return list.isEmpty()?null:list.get(0);
    }
    
    /** 获取类名相同的服务第$(index)个 */
    public static <T> T getService(Class<T> cls, int idx)
    {
        List<T> list = getServiceListPrivate(cls);
        return list.isEmpty()?null:list.get(idx);
    }
    
    /** 获取类名相同的服务列表 */
    public static <T> List<T> getServiceList(Class<T> cls)
    {
        return Collections.unmodifiableList(getServiceListPrivate(cls));
    }
    
    @SuppressWarnings("unchecked")
    private static <T> List<T> getServiceListPrivate(Class<T> cls)
    {
        List<T> list = new ArrayList<T>();
        for (Service service : serviceMap.values())
        {
            if (cls == service.getClass())
            {
                list.add((T)service);
            }
        }
        
        return list;
    }
    
    /** 获取所有的服务列表 */
    public static void removeServiceAll()
    {
        List<Service> serviceList = new ArrayList<Service>(serviceMap.values());
        for (int i=serviceList.size()-1;i>=0;i--)
        {
            //清理操作，从最后一个开始
            try
            {
                Service service = serviceList.get(i);
                service.destroy();
            }
            catch(Exception e)
            {
                Logs.info("系统[%s]-版本[%s]清理时有异常情况发生，由开发人员定位，本地不打印日志", getName(), getVersion());
            }
        }
    }
    
    /**********************************************************************/
    //配置文件设置和获取等
    /**********************************************************************/

    /** 设置配置信息，可能存在相同的配置名称或配置组号，有则返回，无返回null表示成功 */
    public static String addConfig(Config config)
    {
        if (configMap.containsKey(config.getName()))
            return config.getName();
        
        configMap.put(config.getName(), config);
        for (Group group : config.getGroupList())
        {
            if (groupMap.containsKey(group.getId()))
                return group.getId();
            
            groupMap.put(group.getId(), group);
        }
        return null;
    }
    
    /** 设置配置信息，仅一个配置组作为配置的情况 */
    public static void addConfigByGroup(Group group)
    {
        String groupId = group.getId();
        
        Asserts.as(Validates.isNotEmpty(groupId) ? null : "Group未配置组编号不允许增加到全局配置中");
        Asserts.as(!configMap.containsKey(groupId)?null:"Group组编号[%s]作为配置名时有重复", groupId);
        Asserts.as(!groupMap.containsKey(groupId)?null:"Group组编号[%s]有重复", groupId);
        
        Config config = new Config(group.getId());
        config.addGroup(group);
        configMap.put(groupId, config);
        groupMap.put(groupId, group);
    }
    
    /** 删除配置信息，仅一个配置组作为配置的情况 */
    public static void removeConfigByGroupId(String groupId)
    {
        configMap.remove(groupId);
        groupMap.remove(groupId);
    }
    
    /** 获取配置信息 */
    public static Config getConfig(String name)
    {
        return configMap.get(name);
    }
    
    /** 保存配置信息 */
    public static void saveConfig(String name) throws Exception
    {
        Config config = configMap.get(name);
        if (config == null)
            throw new Exception("未找到对应的配置信息");
        
        config.save();
    }
    
    /** 保存配置信息 */
    public static void saveConfigByGroup(String id) throws Exception
    {
        Group group = groupMap.get(id);
        if (group == null)
            throw new Exception("未找到对应的配置信息");
        
        Config config = configMap.get(group.getName());
        if (config == null)
            throw new Exception("未找到对应的配置信息");
        
        config.save();
    }
    
    /** 判断是否有配置组 */
    public static boolean hasGroup(String id)
    {
        return groupMap.containsKey(id);
    }
    
    /** 获取配置组信息 */
    public static Group getGroup(String id)
    {
        return groupMap.get(id);
    }
    
    /** 获取配置组列表 */
    public static Collection<Group> getGroupList()
    {
        return groupMap.values();
    }
    
    /** 判断是否有配置项 */
    public static boolean hasItem(String id, String key)
    {
        Group group = groupMap.get(id);
        if (group == null)
            return false;
        
        return group.hasItem(key);
    }
    
    /** 获取系统引导名称 */
    public static String getName()
    {
        return getString(BOOT, ITEM_NAME, NAME_CN);
    }
    
    /** 获取系统引导版本 */
    public static String getVersion()
    {
        return getString(BOOT, ITEM_VERSION, VERSION);
    }
    
    /** 获取系统引导端口 */
    public static int getPort()
    {
        return getInt(BOOT, ITEM_PORT);
    }
    
    /**
     * 通过id, key获取value
     * 
     * @param id    组号
     * @param key   键值
     * @return str  字符串，如果未找到返回null
     */
    public static String getString(String id, String key)
    {
        Group group = groupMap.get(id);
        if (group == null)
            return null;
        
        return group.getString(key);
    }
    
    /**
     * 通过id, key获取value,如果为null,则返回缺省值
     * 
     * @param id            组号
     * @param key           键值
     * @param defaultValue  缺省值
     * @return str          字符串，如果未找到返回缺省值
     */
    public static String getString(String id, String key, String defaultValue)
    {
        String value = getString(id, key);
        if (value == null)
            return defaultValue;
        else
            return value;
    }
    
    /**
     * 通过id, key获取value
     * 
     * @param id    组号
     * @param key   键值
     * @return int  整型，如果未找到返回-1
     */
    public static int getInt(String id, String key)
    {
        String value = getString(id, key);
        if (value == null)
            return -1;
        else
            return Ints.toInt(value, -1);
    }
    
    /**
     * 通过id, key获取value,如果为null,则返回缺省值
     * 
     * @param id            组号
     * @param key           键值
     * @param defaultValue  缺省值
     * @return int          整型，如果未找到返回缺省值
     */
    public static int getInt(String id, String key, int defaultValue)
    {
        int value = getInt(id, key);
        if (value == -1)
            return defaultValue;
        else
            return value;
    }

    /**
     * 通过id, key获取value
     * 
     * @param id    组号
     * @param key   键值
     * @return long 长整型，如果未找到返回-1
     */
    public static long getLong(String id, String key)
    {
        String value = getString(id, key);
        if (value == null)
            return -1;
        else
            return Longs.toLong(value, -1);
    }
    
    /**
     * 通过id, key获取value,如果为null,则返回缺省值
     * 
     * @param id            组号
     * @param key           键值
     * @param defaultValue  缺省值
     * @return long         整型，如果未找到返回缺省值
     */
    public static long getLong(String id, String key, long defaultValue)
    {
        long value = getLong(id, key);
        if (value == -1)
            return defaultValue;
        else
            return value;
    }
    
    /**
     * 通过id, key判断是否为真
     * 
     * @param id    组号
     * @param key   键值
     * @return      =true则return true，否则return false
     */
    public static boolean isTrue(String id, String key)
    {
        return "true".equalsIgnoreCase(getString(id, key));
    }
    
    /**
     * 通过id, key判断是否为真
     * 
     * @param id    组号
     * @param key   键值
     * @return      =true则return true，否则return false
     */
    public static boolean isTrue(String id, String key, boolean defaultValue)
    {
        String value = getString(id, key);
        if (value == null)
            return defaultValue;
        
        return "true".equalsIgnoreCase(value);
    }
    
    /**
     * 设置value,通过key，注意update=true时才可以修改
     * 
     * @param id    组号
     * @param key   键值
     * @param value 结果值String
     */
    public static void setValue(String id, String key, String value)
    {
        Group group = groupMap.get(id);
        if (group == null)
            return;
        
        Item item = group.item(key);
        if (item != null)
            item.setValue(value);
    }
    
    /**
     * 设置value,通过key
     * 
     * @param id    组号
     * @param key   键值
     * @param value 结果值int型
     */
    public static void setInt(String id, String key, int value)
    {
        setValue(id, key, String.valueOf(value));
    }
    
    /**
     * 设置value,通过key
     * 
     * @param id    组号
     * @param key   键值
     * @param value 结果值long型
     */
    public static void setLong(String id, String key, long value)
    {
        setValue(id, key, String.valueOf(value));
    }
    
    /**
     * 设置value,通过key
     * 
     * @param id    组号
     * @param key   键值
     * @param value 结果值boolean型
     */
    public static void setBoolean(String id, String key, boolean value)
    {
        setValue(id, key, String.valueOf(value));
    }
    
    /**********************************************************************/
    //类简称设置和获取
    /**********************************************************************/
    
    /** 判断是否有短名的类 */
    static boolean hasClass(String alias)
    {
        return aliasMap.containsKey(alias);
    }
    
    /** 设置短名类名称和类结构到全局变量中 */
    public static void addClass(String alias, Class<?> clazz)
    {
        aliasMap.put(alias, clazz);
    }
    
    /** 通过别名，找到对应的有别名的类 */
    public static Class<?> getClass(String alias)
    {
        return aliasMap.get(alias);
    }
    
    /** 获取全局别名类列表 */
    public static Collection<Class<?>> getClassList()
    {
        return aliasMap.values();
    }
    
    /** 获取全局别名类列表 */
    public static Map<String, Class<?>> getClassMap()
    {
        return aliasMap;
    }
    
    /** 清除全局别名列表 */
    public static void clearAliasMap()
    {
        aliasMap.clear();
    }
    
    /** 如果是别名则返回真类名，否则返回当前值 */
    public static String getClassName(String className)
    {
        Class<?> cls = aliasMap.get(className);
        return cls == null?className:cls.getName();
    }
    
    /** 通过类名获取类结构，支持别名，优先别名，再查结构 */
    public static Class<?> forName(String className)
    {
        Class<?> cls = Global.getClass(className);
        if (cls != null)
            return cls;
        
        return Classes.forName(className);
    }
    
    /** 通过类名获取类实例，支持别名，优先别名，再查结构 */
    public static Object newInstance(String className)
    {
        if(className == null)
            return null;
        
        Class<?> cls = Global.getClass(className);
        if (cls != null)
            return Classes.newInstance(cls);
        
        return Classes.newInstance(className);
    }
}
